/*

Archivo: "abstractor.cpp"

Implementación de un "abstractor" para el conjunto.
Permite la instanciación de un conjunto con elementos de
un tipo dado y con una estructura de soporte especificada,
la cuál se conecta con el patrón "bridge".

Implementación de un parser para los comandos del conjunto.

Autor: Diego Luna

Primera versión: 5-6-2012

*/


#include "abstractor.h"

#include <sstream>
#include <vector>


// ++++++++++++++++++++++++ Static Operators - Begin ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// Operador de salida por stream para una variable bool_enum.
static std::ostream& operator<<(std::ostream &oss, const bool_enum &bool_val)
{
	oss << ((bool_val == BE_FALSE)?FALSE_STR:TRUE_STR);

	return oss;
}

// ++++++++++++++++++++++++ Static Operators - End ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Static Functions - Begin ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// Separa un string en "tokens" separados por caracteres que se
// pueden especificar, descartando todo texto de las líneas luego
// de los comentarios que empiezan con caractres que pueden también
// especificarse, y los coloca en un contenedor de strings
// Puede elegirse si se admiten tokens vacíos o no.
// El parámetro de template debe ser un contenedor que soporte
// la operación "push_back" (<vector>, <queue>, <deque>, <list>).
template <typename CONT>
static void tokenize(const std::string& str, CONT& tokens,
	const std::string& delimiters,  const std::string& comment_delimiters,
	const bool trimEmpty)
{
	std::string::size_type pos = 0, lastPos = 0;
	std::string::size_type commentPos = 0;

	// Encuentro el comienzo del comentario en la línea.
	commentPos = str.find_first_of(comment_delimiters);

	// Si la línea comienza en comentario, terminamos acá.
	if (commentPos == 0)
		return;

	// Si la línea no tiene comentario, todo el
	// string debe ser procesado.
	if (commentPos == std::string::npos)
		commentPos = str.length();

	// Recorro todo el string hasta el comentario
	// separando en tokens ante cada delimitador
	// y lo agrego al contenedor.
	while(true)
	{
		// Encuentro la primera ocurrencia de un delimitador.
		pos = str.find_first_of(delimiters, lastPos);

		// Si no encontró un delimitador.
		if(pos == std::string::npos)
		{
			// Llegué al último delimitador.
			pos = commentPos;

			// Si hay un nuevo token, o se admite un token vacío.
			if(pos != lastPos || !trimEmpty)
				tokens.push_back(str.substr(lastPos, pos-lastPos));

			break;
		}
		else
		{
			// Si hay un nuevo token, o se admite un token vacío.
			if(pos != lastPos || !trimEmpty)
				tokens.push_back(str.substr(lastPos, pos-lastPos));
		}

		// Avanzo hasta después del delimitador encontrado.
		lastPos = pos + 1;
	}
}

// Convierte un comando en forma de string al tipo enumerado y
// determina además si necesita parámetro.
static t_commands convert_str_command(std::string cmd, bool& use_par)
{
	t_commands rcmd = tcmd_error;

	use_par = false;

	if (cmd == "add")
	{
		rcmd = tcmd_add;
		use_par = true;
	}
	else if (cmd == "includes")
	{
		rcmd = tcmd_includes;
		use_par = true;
	}
	else if (cmd == "remove")
	{
		rcmd = tcmd_remove;
		use_par = true;
	}
	else if (cmd == "is_empty")
		rcmd = tcmd_is_empty;
	else if (cmd == "size")
		rcmd = tcmd_size;
	else if (cmd == "wipe")
		rcmd = tcmd_wipe;
	else if (cmd == "print")
		rcmd = tcmd_print;

	return rcmd;
}

// ++++++++++++++++++++++++ Static Functions - End ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Constructores\Destructores - [Set_Abstractor] Begin +++++++++++++++++++++++++++++

// Constructor.
Set_Abstractor::Set_Abstractor(t_implementations implementation, t_datatypes data_type):
implementation_(implementation), data_type_(data_type), implementor_(NULL), set_(NULL)
{
	// Creo el "handle" y el "implementor" con el tipo adecuado.
	create_implementation();
}

// Constructor copia.
Set_Abstractor::Set_Abstractor(const Set_Abstractor& set_abs):
implementation_(set_abs.implementation_), data_type_(set_abs.data_type_), implementor_(NULL), set_(NULL)
{
	// Llamo al operador de asignación.
	*this = set_abs;
}

// Destructor.
Set_Abstractor::~Set_Abstractor()
{
	// Destruyo la representación interna.
	destroy_implementation();
}

// ++++++++++++++++++++++++ Constructores\Destructores [Set_Abstractor] - End +++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Métodos públicos [Set_Abstractor] - Begin +++++++++++++++++++++++++++++++++++++++

// Recibe un comando y su parámetro y devuelve un código de error y por referencia el
// string resultado.
byte Set_Abstractor::execute(const t_commands Command, const std::string& Parameter, std::ostream& oss)
{
	// Si no hay comando que ejecutar, no hago nada.
	if (Command == tcmd_nop)
		return COMMAND_EXECUTED;

	// Parámetro int.
	int int_par;

	// Parámetro float.
	float float_par;

	// Parámetro string.
	std::string str_par;

	// Puntero al parámetro.
	void* ppar = NULL;

	// Leo el parámetro si es requerido y tomo el puntero sin tipo del mismo.
	// El parser devuelve un parámetro no vacío solo si es requerido.
	if (Parameter.length() > 0)
	{
		std::istringstream iss(Parameter);

		switch (data_type_)
		{
		case tdtype_float:
			if (!(iss >> float_par))
				return INVALID_PARAMETER;

			ppar = &float_par;

			break;
		case tdtype_string:
			if (!(iss >> str_par))
				return INVALID_PARAMETER;

			ppar = &str_par;

			break;
		case tdtype_int:
		default:

			if (!(iss >> int_par))
				return INVALID_PARAMETER;

			ppar = &int_par;
		}
	}

	// ejecuto el comando indicado.
	execute(Command, ppar, oss);

	// El comando se ejecutó correctamente.
	return COMMAND_EXECUTED;
}

// Devuelve un string que identifica a la clase del contenedor.
std::string Set_Abstractor::class_ID() const
{
	// Devuelve el string que identifica la clase del "implementor".
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetImplementation<float>*>(implementor_)->class_ID();
	case tdtype_string:
		return static_cast<SetImplementation<std::string>*>(implementor_)->class_ID();
	case tdtype_int:
	default:
		return static_cast<SetImplementation<int>*>(implementor_)->class_ID();
	}
}

// ++++++++++++++++++++++++ Métodos públicos [Set_Abstractor] - End +++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Operadores [Set_Abstractor] - Begin ++++++++++++++++++++++++++++++++++++++++++++++

// Operador de asignación.
Set_Abstractor& Set_Abstractor::operator=(const Set_Abstractor& set_abs)
{

	// Verifico la autoasignación.
	if (&set_abs == this)
		return *this;

	// Si varía el tipo de los elementos o la estructura de soporte.
	if ((implementation_ != set_abs.implementation_) || (data_type_ != set_abs.data_type_))
	{
		// Destruyo la implementación.
		destroy_implementation();

		// Copio el tipo de dato de los elementos y 
		// la implementación del original.
		implementation_ = set_abs.implementation_;
		data_type_ = set_abs.data_type_;

		// Recreo la implementación.
		create_implementation();
	}

	// Copio el conjunto (ahora incluyen elementos del mismo tipo).
	switch (data_type_)
	{
	case tdtype_float:
		*(static_cast<SetHandle<float>*>(set_)) = *(static_cast<SetHandle<float>*>(set_abs.set_));
		break;
	case tdtype_string:
		*(static_cast<SetHandle<std::string>*>(set_)) = *(static_cast<SetHandle<std::string>*>(set_abs.set_));
		break;
	case tdtype_int:
	default:
		*(static_cast<SetHandle<int>*>(set_)) = *(static_cast<SetHandle<int>*>(set_abs.set_));
		break;
	}

	return *this;
}

// Operador de comparación por igualdad.
bool Set_Abstractor::operator==(const Set_Abstractor& set_abs)
{
	// Si contienen elementos de tipo distinto no pueden ser iguales.
	if (data_type_ != set_abs.data_type_)
		return false;

	// Comparo los elemntos contenidos sin importar la estructura de soporte de cada conjunto.
	switch (data_type_)
	{
	case tdtype_float:
		return (*(static_cast<SetHandle<float>*>(set_)) == *(static_cast<SetHandle<float>*>(set_abs.set_)));
		break;
	case tdtype_string:
		return (*(static_cast<SetHandle<std::string>*>(set_)) == *(static_cast<SetHandle<std::string>*>(set_abs.set_)));
		break;
	case tdtype_int:
	default:
		return (*(static_cast<SetHandle<int>*>(set_)) == *(static_cast<SetHandle<int>*>(set_abs.set_)));
		break;
	}	

}

// Operador de comparación por desigualdad.
bool Set_Abstractor::operator!=(const Set_Abstractor& set_abs)
{
	return (!(*this == set_abs));
}

// ++++++++++++++++++++++++ Operadores [Set_Abstractor] - End ++++++++++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Métodos privados [Set_Abstractor] - Begin +++++++++++++++++++++++++++++++++++++++

// Executa un comando especificado imprimiendo el resultado..
void Set_Abstractor::execute(const t_commands Command, const void* Parameter, std::ostream& oss)
{
	// ejecuto el comando.
	switch (Command)
	{
	case tcmd_add:
		add(Parameter);
		break;
	case tcmd_includes:
		oss << includes(Parameter) << std::endl;
		break;
	case tcmd_remove:
		remove(Parameter);
		break;
	case tcmd_is_empty:
		oss << is_empty() << std::endl;
		break;
	case tcmd_wipe:
		wipe();
		break;
	case tcmd_size:
		oss << size() << std::endl;
		break;
	case tcmd_print:
		print(oss);
		oss << std::endl;
		break;
	default:
		;
	}
}

// Construye el "handle" y el "implementor".
void Set_Abstractor::create_implementation()
{
	switch (implementation_)
	{
	case timp_tree:
		implementor_ = create_tree_implementor(data_type_);
		set_ = create_set_handle(data_type_, implementor_);
		break;
	case timp_hash:
		implementor_ = create_hashtable_implementor(data_type_);
		set_ = create_set_handle(data_type_, implementor_);
		break;
	case timp_list:
	default:
		implementor_ = create_list_implementor(data_type_);
		set_ = create_set_handle(data_type_, implementor_);
		break;
	}
}

// Detruye el "handle" y el "implementor".
void Set_Abstractor::destroy_implementation()
{
	// SetImplementation<T> tiene un destructor virtual, por ende está
	// definida la llamada a delete del puntero a esta clase base.

	switch (data_type_)
	{
	case tdtype_float:
		delete static_cast<SetHandle<float>*>(set_);
		delete static_cast<SetImplementation<float>*>(implementor_);
		break;
	case tdtype_string:
		delete static_cast<SetHandle<std::string>*>(set_);
		delete static_cast<SetImplementation<std::string>*>(implementor_);
		break;
	case tdtype_int:
	default:
		delete static_cast<SetHandle<int>*>(set_);
		delete static_cast<SetImplementation<int>*>(implementor_);
		break;
	}

	set_ = NULL;
	implementor_ = NULL;
}

// Agrega un elemento al contenedor.
bool_enum Set_Abstractor::add(const void* key)
{
	// La sintáxis en este y los métodos siguientes es muy desagradable,
	// lo que me hace pensar que debería usar alguna otra forma de hacer esto.
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetHandle<float>*>(set_)->add(*(static_cast<const float*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_string:
		return static_cast<SetHandle<std::string>*>(set_)->add(*(static_cast<const std::string*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_int:
	default:
		return static_cast<SetHandle<int>*>(set_)->add(*(static_cast<const int*>(key)))?BE_TRUE:BE_FALSE;
	}
}

// Elimina un elemento del contenedor (si se encuentra).
bool_enum Set_Abstractor::remove(const void* key)
{
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetHandle<float>*>(set_)->remove(*(static_cast<const float*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_string:
		return static_cast<SetHandle<std::string>*>(set_)->remove(*(static_cast<const std::string*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_int:
	default:
		return static_cast<SetHandle<int>*>(set_)->remove(*(static_cast<const int*>(key)))?BE_TRUE:BE_FALSE;
	}
}

// Determina si un elemento se encuentra en el contenedor.
bool_enum Set_Abstractor::includes(const void* key) const
{
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetHandle<float>*>(set_)->includes(*(static_cast<const float*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_string:
		return static_cast<SetHandle<std::string>*>(set_)->includes(*(static_cast<const std::string*>(key)))?BE_TRUE:BE_FALSE;
	case tdtype_int:
	default:
		return static_cast<SetHandle<int>*>(set_)->includes(*(static_cast<const int*>(key)))?BE_TRUE:BE_FALSE;
	}
}

// Vacía el contenedor.
void Set_Abstractor::wipe()
{
	switch (data_type_)
	{
	case tdtype_float:
		static_cast<SetHandle<float>*>(set_)->wipe();
		break;
	case tdtype_string:
		static_cast<SetHandle<std::string>*>(set_)->wipe();
		break;
	case tdtype_int:
	default:
		static_cast<SetHandle<int>*>(set_)->wipe();
		break;
	}
}

// Determina si el contenedor esta vacío.
bool Set_Abstractor::is_empty() const
{
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetHandle<float>*>(set_)->is_empty();
	case tdtype_string:
		return static_cast<SetHandle<std::string>*>(set_)->is_empty();
	case tdtype_int:
	default:
		return static_cast<SetHandle<int>*>(set_)->is_empty();
	}
}

// Devuelve la cantidad de elementos en el contenedor.
size_t Set_Abstractor::size() const
{
	switch (data_type_)
	{
	case tdtype_float:
		return static_cast<SetHandle<float>*>(set_)->size();
	case tdtype_string:
		return static_cast<SetHandle<std::string>*>(set_)->size();
	case tdtype_int:
	default:
		return static_cast<SetHandle<int>*>(set_)->size();
	}
}

// Imprime los elementos contenidos.
void Set_Abstractor::print(std::ostream& oss) const
{
	switch (data_type_)
	{
	case tdtype_float:
		static_cast<SetHandle<float>*>(set_)->print(oss);
		break;
	case tdtype_string:
		static_cast<SetHandle<std::string>*>(set_)->print(oss);
		break;
	case tdtype_int:
	default:
		static_cast<SetHandle<int>*>(set_)->print(oss);
		break;
	}
}

// Crea una especialización del template "implementor" árbol con el tipo dado.
void* Set_Abstractor::create_tree_implementor(t_datatypes data_type)
{
	switch (data_type)
	{
	case tdtype_float:
		return (new Binary_Search_Tree<float>());
	case tdtype_string:
		return (new Binary_Search_Tree<std::string>());
	case tdtype_int:
	default:
		return (new Binary_Search_Tree<int>());
	}
}

// Crea una especialización del template "implementor" lista con el tipo dado.
void* Set_Abstractor::create_list_implementor(t_datatypes data_type)
{
	switch (data_type)
	{
	case tdtype_float:
		return (new Double_Linked_List<float>());
	case tdtype_string:
		return (new Double_Linked_List<std::string>());
	case tdtype_int:
	default:
		return (new Double_Linked_List<int>());
	}
}

// Crea una especialización del template "implementor" tabla de hash con el tipo dado.
void* Set_Abstractor::create_hashtable_implementor(t_datatypes data_type)
{
	switch (data_type)
	{
	case tdtype_float:
		return (new Hash_Table_Murmur<float>());
	case tdtype_string:
		return (new Hash_Table_Murmur<std::string>());
	case tdtype_int:
	default:
		return (new Hash_Table_Murmur<int>());
	}
}

// Crea una especialización del template "handle" con el tipo dado.
void* Set_Abstractor::create_set_handle(t_datatypes data_type, void* implementor)
{
	switch (data_type)
	{
	case tdtype_float:
		return (new SetHandle<float>(static_cast<SetImplementation<float>*>(implementor)));
	case tdtype_string:
		return (new SetHandle<std::string>(static_cast<SetImplementation<std::string>*>(implementor)));
	case tdtype_int:
	default:
		return (new SetHandle<int>(static_cast<SetImplementation<int>*>(implementor)));
	}
}

// ++++++++++++++++++++++++ Métodos privados [Set_Abstractor] - End +++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Constructores\Destructores [Set_Command_Parser] - Begin +++++++++++++++++++++++++

// Constructor.
Set_Command_Parser::Set_Command_Parser()
{
}

// Destructor.
Set_Command_Parser::~Set_Command_Parser()
{
}

// ++++++++++++++++++++++++ Constructores\Destructores [Set_Command_Parser] - End +++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Métodos públicos [Set_Command_Parser] - Begin +++++++++++++++++++++++++++++++++++++++

// Parsea el string dado, devuelve código de error.
byte Set_Command_Parser::parse(const std::string & LineBuffer, t_commands& Command, std::string & Parameter)
{
	// El comando usa parámetro.
	bool use_par = false;

	// Parámetro devuelto.
	Parameter = "";

	// Vector de tokens parseados.
	std::vector<std::string> tokens;

	// Parseo la línea leída en tokens.
	tokenize<std::vector<std::string> >(LineBuffer, tokens, " \t", "",  true);

	// Si la línea leída no contiene comandos, se ignora.
	if (tokens.size() < 1)
	{
		Command = tcmd_nop;
		return COMMAND_PARSED;//COMMAND_EXPECTED;
	}

	// Convierto el comando string leído al tipo enumerado y obtengo
	// además si el mismo necesita parámetro.
	Command = convert_str_command(tokens[0], use_par);

	// No se reconoció el comando leído.
	if (Command == tcmd_error)
		return UNKNOWN_COMMAND;

	// No se paso el parámetro  requerido.
	if ((tokens.size() != 2) && use_par)
		return PARAMETER_EXPECTED;

	// Parametro no requerido fué pasado.
	if ((tokens.size() != 1) && !use_par)
		return PARAMETER_NOT_EXPECTED;

	// Devuelvo el parámetro si es requerido.
	if (use_par)
		Parameter = tokens[1];

	// El comando se parseo correctamente (excepto el parámetro0.
	return COMMAND_PARSED;
}

// ++++++++++++++++++++++++ Métodos públicos [Set_Command_Parser] - End +++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++ Public Functions - Begin ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// Devuelve la descripción de los errores devueltos por el 
// "Set_Abstractor" y el "Set_Command_Parser".
std::string get_error_description(byte Error_Number)
{
	switch (Error_Number)
	{
	case COMMAND_EXPECTED:
		return ERROR_COMMAND_MISSING;
	case UNKNOWN_COMMAND:
		return ERROR_COMMAND_UNKNOWN;
	case PARAMETER_EXPECTED:
		return ERROR_PARAMETER_MISSING;
	case PARAMETER_NOT_EXPECTED:
		return ERROR_PARAMETER_NOT_REQUIRED;
	case INVALID_PARAMETER:
		return ERROR_INVALID_PARAMETER;
	default:
		return "";
	}
}

// ++++++++++++++++++++++++ Public Functions - End ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


